package com.example.demo.util;


import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
//import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.ssl.TrustStrategy;
import org.apache.http.util.EntityUtils;
import org.springframework.util.StringUtils;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Base64;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;

/**
 * @program: ZT
 * @description: 计算签名
 * @author: zt
 * @create: 2022-02-24 10:42
 **/
public class ZTApiSDKUtil {
    // 应用id
    static final String APP_ID = "test";
    // 应用秘钥
    static final String APP_SECRET = "test";
    // {"grant_type":"client_credentials","client_id":"test","client_secret":"test"}
    static final String GET_TOKEN_URL = "http://10.100.20.13:8000/test/oauth2/token";

    static final String REQ_URL = "http://10.100.20.13:8000/test?id=123";

    static final String REQ_BODY = "{}";

    public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeyException, IOException, URISyntaxException {
        String token = getToken();
        System.out.println("token:"+token);
//        String resultPost = httpPostClient(REQ_URL, token, REQ_BODY); PwbGXFa7bcpBjfGohGpPdBefgn48R2PJ
//        System.out.println("resultPost:"+resultPost);
        String resultGet = httpGetClient(REQ_URL, token);
        System.out.println("resultGet:"+resultGet);
//        System.out.println(getRequestLine("POST","https://10.100.12.23:8443/kong-test/kong-test/post"));
//        httpPostClient("https://10.100.4.16:8443/kong-test/kong-test/post2","YtAqu1hpEYuDaGVjFCbxSOWirf71y3Qc","{\"grant_type\":\"client_credentials\",\"client_id\":\"test\",\"client_secret\":\"test\"}");
    }

    /**
     * POST 请求
     * @param url
     * @param token
     * @param body
     * @return
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeyException
     * @throws IOException
     */
    public static String httpPostClient(String url,String token,String body) throws NoSuchAlgorithmException, InvalidKeyException, IOException {
        CloseableHttpClient httpClient = createClientDefault(url);
        HttpPost httpPost = new HttpPost(url);
        // 获取请求行
        String requestLine = getRequestLine(httpPost);
        Map<String, String> signHeaders = getSignHeaders(APP_SECRET,null, requestLine, body);
        httpPost.setHeader("Content-Type","application/json");
        for (Map.Entry<String, String> signHeader: signHeaders.entrySet()) {
            httpPost.addHeader(signHeader.getKey(),signHeader.getValue());
        }
        httpPost.addHeader("token",token);
        StringEntity stringEntity = new StringEntity(body, "UTF-8");
        httpPost.setEntity(stringEntity);
        HttpResponse response = httpClient.execute(httpPost);
        return EntityUtils.toString(response.getEntity());
    }



    /**
     * GET 请求
     * @param url
     * @param token
     * @return
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeyException
     * @throws IOException
     */
    public static String httpGetClient(String url,String token) throws NoSuchAlgorithmException, InvalidKeyException, IOException {
        CloseableHttpClient httpClient = createClientDefault(url);
        HttpGet httpGet = new HttpGet(url);
        // 获取请求行
        String requestLine = getRequestLine(httpGet);
        Map<String, String> signHeaders = getSignHeaders(APP_SECRET, null,requestLine,null);
        for (Map.Entry<String, String> signHeader: signHeaders.entrySet()) {
            httpGet.addHeader(signHeader.getKey(),signHeader.getValue());
        }
        httpGet.addHeader("token",token);

        HttpResponse response = httpClient.execute(httpGet);
        return EntityUtils.toString(response.getEntity());
    }

    public static Map<String, String> httpGetClientHeaders(String url,String token) throws NoSuchAlgorithmException, InvalidKeyException, IOException {
        CloseableHttpClient httpClient = createClientDefault(url);
        HttpGet httpGet = new HttpGet(url);
        // 获取请求行
        String requestLine = getRequestLine(httpGet);
        Map<String, String> signHeaders = getSignHeaders(APP_SECRET, null,requestLine,null);
        signHeaders.put("token",token);
        return signHeaders;
    }

    public static Map<String,String> getDebugSignHeaders(String secret,String method, String url,String body) throws InvalidKeyException, NoSuchAlgorithmException, UnsupportedEncodingException, URISyntaxException {
        // 获取请求行
        String requestLine = getRequestLine(method,url);
        return getSignHeaders(secret,null,requestLine,body);
    }

    private static String getRequestLine(String method, String url) throws URISyntaxException {
        HttpRequestBase requestBase = new HttpRequestBase() {
            @Override
            public String getMethod() {
                return method;
            }
        };
        requestBase.setURI(new URI(url));
        return getRequestLine(requestBase);
    }

    /**
     * 获取token
     * @return
     * @throws IOException
     */
    public static String getToken() throws IOException {
        CloseableHttpClient httpClient = createClientDefault(GET_TOKEN_URL);
        HttpPost httpPost = new HttpPost(GET_TOKEN_URL);
        AccessTokenGet tokenGet = new AccessTokenGet();
        tokenGet.setClient_id(APP_ID);
        tokenGet.setClient_secret(APP_SECRET);
        StringEntity stringEntity = new StringEntity(JsonUtils.toJsonString(tokenGet), "UTF-8");
        httpPost.setEntity(stringEntity);
        httpPost.setHeader("Content-Type","application/json");
        HttpResponse response = httpClient.execute(httpPost);
        String json = EntityUtils.toString(response.getEntity());
        AccessToken accessToken = JsonUtils.parseBean(json, AccessToken.class);
        if(accessToken==null){
            return null;
        }
        return accessToken.access_token;
    }

    /**
     * 获取请求行
     * $request-line（请求行）参数获取说明，请求行包含三部分组成：请求方式，请求URL，请求协议。通过空格拼接。request-line获取示例：
     *
     * 请求地址=POST http://192.168.56.105:8000/kong-test/kong-test/get?id=name&name=122 HTTP/1.1
     * request-line=POST /kong-test/kong-test/post?id=name&name=122 HTTP/1.1
     * @param httpRequestBase
     * @return
     */
    private static String getRequestLine(HttpRequestBase httpRequestBase) {
        StringBuilder requestLine = new StringBuilder(httpRequestBase.getMethod())
                .append(" ").append(httpRequestBase.getURI().getPath());
        if(!StringUtils.isEmpty(httpRequestBase.getURI().getQuery())){
            requestLine.append("?").append(httpRequestBase.getURI().getQuery());
        }
        requestLine.append(" ").append(httpRequestBase.getProtocolVersion().toString());
        return requestLine.toString();
    }

    /**
     * 获取签名header头
     * @param secret 应用秘钥
     * @param date 请求时间戳，可以为空。默认取当前时间
     * @param body 请求body,可以为空
     * @param requestLine 请求行，格式：POST /kong-test/kong-test/get?id=name&name=122 HTTP/1.1
     * @return
     * @throws InvalidKeyException
     * @throws NoSuchAlgorithmException
     * @throws UnsupportedEncodingException
     */
    public static Map<String,String> getSignHeaders(String secret,Long date,String requestLine,String body) throws InvalidKeyException, NoSuchAlgorithmException, UnsupportedEncodingException {
        Map<String,String> map = new HashMap<>();
        // 设置默认时间
        if(date==null){
            date = System.currentTimeMillis();
        }
        // 获取签名内容
        String content = getSignatureContent(date,requestLine,body);
        System.out.println("content:"+content);
        // 计算签名
        String signature = calculationSignature(content, secret);
        // 赋值
        map.put("date",date.toString());
        map.put("sign",signature);
        return map;
    }

    /**
     * 步骤一：拼接签名内容(content)
     * 对请求时间戳，请求行，请求体信息进行拼接，通过\n分割
     *
     * 拼接格式：
     *
     * $date\n$request-line\n$request-body
     * @param date
     * @param requestLine
     * @param body
     * @return
     */
    private static String getSignatureContent(long date, String requestLine, String body) {
        StringBuilder content = new StringBuilder();
        content.append(date).append("\n")
                .append(requestLine);
        if(StringUtils.isEmpty(body)){
            return content.toString();
        }
        content.append("\n").append(body);
        return content.toString();
    }

    /**
     * 计算签名
     * @param content
     * @param secret
     * @return
     * @throws InvalidKeyException
     * @throws NoSuchAlgorithmException
     * @throws UnsupportedEncodingException
     */
    private static String calculationSignature(String content, String secret) throws InvalidKeyException, NoSuchAlgorithmException, UnsupportedEncodingException  {
        return new String(Base64.getEncoder().encode(signatureReturnBytes(content, secret)), "US-ASCII");
    }

    public static byte[] signatureReturnBytes(String content, String secret) throws NoSuchAlgorithmException, InvalidKeyException {
        SecretKeySpec signingKey = new SecretKeySpec(secret.getBytes(), "HmacSHA256");
        Mac mac = Mac.getInstance("HmacSHA256");
        mac.init(signingKey);
        return mac.doFinal(content.getBytes());
    }

    /**
     * base64(sha256)
     * @param digest
     * @return
     */
    public static String encryptDigest(String digest){
        return Base64.getEncoder().encodeToString(sha256(digest));
    }

    /**
     * sha256
     * @param strText
     * @return
     */
    public static byte[] sha256(String strText) {
        if (strText != null && strText.length() > 0) {
            try {
                MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
                messageDigest.update(strText.getBytes());
                return messageDigest.digest();
            } catch (NoSuchAlgorithmException e) {
                return null;
            }
        }else{
            return null;
        }
    }

    public static CloseableHttpClient createClientDefault(String url) {
        if(StringUtils.isEmpty(url)||url.indexOf("https://")==-1){
            return HttpClients.createDefault();
        }
        return createSSLClientDefault();
    }

    public static CloseableHttpClient createSSLClientDefault() {
        try {
            //使用 loadTrustMaterial() 方法实现一个信任策略，信任所有证书
            SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
                // 信任所有
                @Override
                public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                    return true;
                }
            }).build();
            //NoopHostnameVerifier类:  作为主机名验证工具，实质上关闭了主机名验证，它接受任何
            //有效的SSL会话并匹配到目标主机。
            HostnameVerifier hostnameVerifier = NoopHostnameVerifier.INSTANCE;
            SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext, hostnameVerifier);
            return HttpClients.custom().setSSLSocketFactory(sslsf).build();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return HttpClients.createDefault();

    }

    public static class AccessToken {
        /**
         * token
         */
        private String access_token;
        /**
         *
         */
        private String token_type;
        /**
         * 过期时间
         */
        private String expires_in;

        public AccessToken() {
        }

        public String getAccess_token() {
            return access_token;
        }

        public void setAccess_token(String access_token) {
            this.access_token = access_token;
        }

        public String getToken_type() {
            return token_type;
        }

        public void setToken_type(String token_type) {
            this.token_type = token_type;
        }

        public String getExpires_in() {
            return expires_in;
        }

        public void setExpires_in(String expires_in) {
            this.expires_in = expires_in;
        }
    }

    public static class AccessTokenGet {
        /**
         * 认证类型
         */
        private String grant_type = "client_credentials";
        /**
         * 应用id
         */
        private String client_id;
        /**
         * 应用秘钥
         */
        private String client_secret;

        public AccessTokenGet() {
        }

        public String getGrant_type() {
            return grant_type;
        }

        public void setGrant_type(String grant_type) {
            this.grant_type = grant_type;
        }

        public String getClient_id() {
            return client_id;
        }

        public void setClient_id(String client_id) {
            this.client_id = client_id;
        }

        public String getClient_secret() {
            return client_secret;
        }

        public void setClient_secret(String client_secret) {
            this.client_secret = client_secret;
        }
    }

    public static class JsonUtils {
        private static ObjectMapper MAPPER;

        static {
            MAPPER = new ObjectMapper();
            MAPPER.setLocale(Locale.CHINA);

            SimpleModule simpleModule = new SimpleModule();
            simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance);
            simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
            MAPPER.registerModule(simpleModule);
            //设置忽略不存在的字段
            MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
            //要设置
        }


        public static String toJsonString(Object value) {
            try {
                return MAPPER.writeValueAsString(value);
            } catch (JsonProcessingException e) {
                throw new RuntimeException(e);
            }
        }

        public static <T> T parseBean(String content, Class<T> valueType) {
            try {
                return MAPPER.readValue(content, valueType);
            } catch (JsonProcessingException e) {
                throw new RuntimeException(e);
            }
        }

        public static <T> T parseBean(String content, TypeReference<T> ref) {
            try {
                return MAPPER.readValue(content, ref);
            } catch (JsonProcessingException e) {
                throw new RuntimeException(e);
            }
        }

        public static <T> T instanceBean(TypeReference<T> ref) {
            JavaType javaType = MAPPER.constructType(ref.getType());
            return javaType.getTypeHandler();
        }
    }
}